#ifndef GENERALIZATION_H
#define GENERALIZATION_H

#include <thrust/device_vector.h>
#include <thrust/transform_reduce.h>
#include <thrust/functional.h>
#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
using namespace thrust;

#define MAX_DIM 128			// max dimension size of keyword
#define MAX_DIM_VALUE 256	// max dimension value of keyword

#define THREAD_PER_BLK 128  // must be greater or equal to MAX_DIM and not exceed 1024

struct Result{
	int query;		// query ID
	int feature_id;	// feature ID
	float count;	// accumulate distance
	int lb;			// lower bound 
	int ub;			// upper bound

	__device__ __host__ 
	void print_result_entry(){
		printf("%3d %5d %2d %9d %9d",this->query,this->feature_id,(int)this->count,this->lb,this->ub);
	}
};


typedef int InvlistEnt; // the entry of a inverted list, this number must not exceed MAX_FEATURE_ID

// the entry of mapping from query to feature
struct QueryFeatureEnt{
	float count; // under current setup, count is actually integer and max is 128
	int ACD; // accumulated distance

	__host__ __device__ QueryFeatureEnt(){
		count = 0;
		ACD = 0;
	}
};

// keep track of the last positions the counting computed
struct QueryPos{
	int2 pos[MAX_DIM]; // pos[i].x means the down moved step, pos[i].y means the up moved step

	__host__ __device__ QueryPos(){
		for(int i = 0; i < MAX_DIM; i++){
			pos[i].x = 0;
			pos[i].y = 0;
		}
	}
};

// device constants
__constant__ int D_MAX_FEATURE_ID;		// maximum of feature ID
__constant__ int D_DIST_FUNCTION_TYPE;	// distance function type: 1. Educlidean Distance	2. Manhattan Distance


class QueryManager{
public:
	/*	
	inv_list_dir:			the directory contains the invert list folder and a file storing the length of each invert list file.
	distance_func:			1. Educlidean Distance	2. Manhattan Distance
	*/
	QueryManager(string inv_list_dir,int distance_func=1);

	/*
	query_file_path:path storing the query file, if this field is "", then random generation will be invoked
	num_of_query:	number of random queries
	N:				number of documents to count towards up and down position along a dimension
	lb:				lower bound for searching along a dimension
	ub:				upper bound for searching along a dimension
	threashold:		threshold for outputing result
	K;				topK to select
	*/
	void init_new_query(string query_file_path,int num_of_query,int N,int lb,int ub,float threshold, int K);

	// for the thread control, stop_value == 1, then the query will stop
	bool run_query(vector<Result>& result);

	/* clear all gpu memory */
	~QueryManager();

private:
	/* ------------------------------init functions ---------------------------------*/
	// random generate inverted list
	void rand_inv_list();
	// random generate query
	void rand_query(int query_num);

	// retrieve invert list given the directory
	void get_invert_list_from_file(string data_root_dir);
	// retrieve invert list given the query file
	void get_query_from_file(string query_file_name);

	// clear memory to setup new round of query search
	void clear_query_memory();

	/* -------------------------------- fields --------------------------------------*/

	// inverted list and the index
	device_vector<InvlistEnt>* d_invert_list; 
	device_vector<int>* d_invert_list_idx;

	// query to keyword: each query associate with 128 keywords strictly
	device_vector<int>* d_query_keyword;

	// result of query to feature result
	device_vector<QueryFeatureEnt>* d_query_feature;

	// positions of last counting
	device_vector<QueryPos>* d_query_pos;

	// record which query is still running, a entry is 1 if it is still running, 0 means the query has stopped
	device_vector<int>* d_valid_query;

	// record for each query, which dimension to compute and index
	device_vector<int>* d_query_search_dims;
	device_vector<int>* d_query_search_dims_idx;

	// number of rounds that the query has been processed
	int num_of_rounds;

	// maximum of the feature input (document ID)
	int MAX_FEATURE_ID;

	// distance function
	int distance_func;

	// the status for query is initialized
	bool query_initialized;

	int N;				// number of documents to count towards up and down position along a dimension
	int lb;				// lower bound for searching along a dimension
	int ub;				// upper bound for searching along a dimension
	float threshold;	// threshold for outputing result
	int K;				// topK to select

	/* ----------------------- helper functions (DEBUG) ------------------------------*/
	
	// print query to keyword relationship and invert list
	void print_problem();
	// print result that returned by GPU
	void print_final_result(vector<Result>& result);
};

#endif